//@author A0097972J
package logic;

import gui.IUserInterface;
import hirondelle.date4j.DateTime;

import java.util.HashMap;

import common.AttributeType;
import common.CommandType;
import common.DateHelper;
import common.Task;
import common.TaskStatus;
import common.TaskType;

import memory.Database;

/**
 * CreateCommandHandler class<br/>
 * This class contains all the logic for Create command, which will create a new
 * Task into the database</br>
 * 
 */

public class CreateCommandHandler extends CommandHandler {
	private final String CREATE_SUCESSFUL = "\nTask is created!\n%1$s \n";
	private final String CREATE_ERROR = "\nError occured while creating %1$s\n";
	private final String CREATE_EXIST = "\n%1$s exists or the time is blocked.\n";
	private final String CHECK_INPUT = "\nSyntax error. Press F10 for more information\n";
	private final String DATE_WARNING = "\n[Warning!] Ending date is defined before current date.\n";
	private final String DATE_LOGIC_ERROR = "\n\nError, start date is after due date\n";
	private final String DATE_ERROR = "\nThe date cannot be converted.\n";

	public CreateCommandHandler(IUserInterface ui, String originalCommand, HashMap<AttributeType, String> attrList) {
		super(ui, originalCommand, attrList);
	}

	/**
	 * Needs to be explicitly called for the command to be started <br/>
	 * Checking for any restriction on storage side will be done in
	 * writeToStorage()
	 */
	public void start() {
		execute();
	}

	/**
	 * Check for storage constraint when creating new Task <br/>
	 * The new Task cannot duplicate with any existing Task, and taskName must
	 * not be empty <br/>
	 * 
	 * @param newTask
	 *            the task need to be checked
	 * @return false is no violation found, otherwise true
	 */
	private boolean isViolatingStorageConstraint(Database storage, Task newTask) {

		if (newTask.getName() == null || storage.isTaskExist(newTask)) {
			return true;
		}
		if (storage.isCollideWithExistingTask(newTask)) {
			return true;
		}

		return false;
	}

	//@author A0088498A
	/**
	 * Execute the create command <br/>
	 */
	private void execute() {
		// Extract and convert into known TaskType
		String type = _attrList.get(AttributeType.type);
		TaskType taskType = TaskType.getValueOf(type);

		// Guess the TaskType if invalid
		if (taskType == TaskType.invalid) {
			String startingTime = _attrList.get(AttributeType.startingTime);
			String endingTime = _attrList.get(AttributeType.endingTime);
			String range = _attrList.get(AttributeType.range);
			taskType = determineTaskType(startingTime, endingTime, range);
		}

		// Route logic: call the appropriate method to process
		switch (taskType) {
		case timed:
			processTimedTask();
			break;
		case deadline:
			processDeadlineTask();
			break;
		case floating:
			processFloatingTask();
			break;
		case invalid:
			// Still invalid then something went wrong
			writeToConsole(CHECK_INPUT);
			pushToHistoryAsNonExecutableCommand();
			break;
		default:
			break;
		}
	}

	//@author A0097972J
	/**
	 * For TimedTask - process start and end date then pass to storage
	 */
	private void processTimedTask() {
		DateTime startDate = getDateTime(_attrList.get(AttributeType.startingTime));
		DateTime endDate = getDateTime(_attrList.get(AttributeType.endingTime));

		if (endDate == null && startDate != null) {
			DateHelper dh = new DateHelper();
			if (_attrList.get(AttributeType.range) != null) {
				endDate = dh.getPlusDateTime(startDate, _attrList.get(AttributeType.range));
			}
		}

		if (startDate != null && endDate != null) {
			if (startDate.compareTo(endDate) < 1) {
				writeToStorage(_attrList.get(AttributeType.name), TaskType.timed, _attrList.get(AttributeType.description), startDate, endDate);
			} else {
				pushToHistoryAsNonExecutableCommand();
				writeToConsole(DATE_LOGIC_ERROR);
			}
		} else {
			pushToHistoryAsNonExecutableCommand();
			writeToConsole(DATE_ERROR);
		}
	}

	/**
	 * For DeadlineTask - process end date and pass to storage
	 */
	private void processDeadlineTask() {
		DateTime endDate = getDateTime(_attrList.get(AttributeType.endingTime));

		if (endDate == null) {
			DateHelper dh = new DateHelper();
			endDate = dh.alternateKeywordParser(_attrList.get(AttributeType.endingTime));
		}

		if (endDate != null) {
			writeToStorage(_attrList.get(AttributeType.name), TaskType.deadline, _attrList.get(AttributeType.description), null, endDate);
		} else {
			pushToHistoryAsNonExecutableCommand();
			writeToConsole(DATE_ERROR);
		}
	}

	/**
	 * For FloatingTask - just pass to storage without processing start and end
	 * date
	 */
	private void processFloatingTask() {
		writeToStorage(_attrList.get(AttributeType.name), TaskType.floating, _attrList.get(AttributeType.description), null, null);
	}

	/**
	 * Create task in Storage <br/>
	 * Add create record in History <br/>
	 * Print status to MainConsole <br/>
	 * 
	 * @param name
	 * @param type
	 * @param description
	 * @param startingTime
	 * @param endingTime
	 */
	private void writeToStorage(String name, TaskType type, String description, DateTime startingTime, DateTime endingTime) {

		Task createTask = new Task(name, type, description, startingTime, endingTime);

		if (!isViolatingStorageConstraint(getStorage(), createTask)) {

			DateHelper dh = new DateHelper();
			if (dh.compareToCurrentDateTime(endingTime) == -1) {
				createTask.setStatus(TaskStatus.overdue);
			}

			boolean isCreated = getStorage().createTask(createTask);

			if (isCreated) {
				// Task created
				pushCommandToHistory(CommandType.add, createTask);
				String createSucessful = String.format(CREATE_SUCESSFUL, createTask.toString(true));
				writeToConsole(createSucessful);
				// Show warning if end date is before current date
				if (dh.compareToCurrentDateTime(endingTime) < 0) {
					writeToConsole(DATE_WARNING);
				}
			} else {
				// Create unsuccessful
				String createError = String.format(CREATE_ERROR, createTask.toString(true));
				pushToHistoryAsNonExecutableCommand();
				writeToConsole(createError);
			}

		} else {
			// Task exists
			String createExist = String.format(CREATE_EXIST, createTask.toString(true));
			pushToHistoryAsNonExecutableCommand();
			writeToConsole(createExist);
		}
	}
}
